#include <stdio.h>
#include <string.h>
#include "bitmap.h"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

// Load and convert image to grayscale
unsigned char *load_image(const char * const image_path,
                          int *width,
                          int *height)
{
    int w,  h,  comp;
    printf("stbi_load %s\n", image_path);
    unsigned char *image_data = stbi_load(image_path,  &w,  &h,  &comp,  0);

    printf("w = %d, h = %d, comp = %d, malloc buffer size = %d\n",
        w, h, comp, (int)sizeof(unsigned char) * w * h);
    unsigned char *gs_image_data = (unsigned char *)malloc(sizeof(unsigned char) * w * h);

    printf("get gs_image data\n");
    if (comp == 1) {
        memcpy(gs_image_data,  image_data,  sizeof(unsigned char) * w * h);
    } else if (comp == 2) {
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                gs_image_data[y * w + x] = image_data[(y * w * 2) + (x * 2)];
            }
        }
    } else if (comp >= 3) {
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                unsigned char r = image_data[(y * w * comp) + (x * comp)];
                unsigned char g = image_data[(y * w * comp) + (x * comp) + 1];
                unsigned char b = image_data[(y * w * comp) + (x * comp) + 2];

                gs_image_data[y * w + x] = (r + g + b) / 3;
            }
        }
    }
    printf("free image_data\n");
    stbi_image_free(image_data);

    *width = w;
    *height = h;
    return gs_image_data;
}

void set_bit(unsigned char *byte, const int i, const int bit)
{
    assert(byte != NULL);
    assert(i >= 0 && i < 8);

    if (bit > 0) {
        *byte |= 1 << i;
    } else {
        *byte &= ~(1 << i);
    }
}

// Calculates the padding required so that the size fits in 32 bits
void calculate_padding(const int size, int *padding_l, int *padding_r)
{
    assert(padding_l != NULL);
    assert(padding_r != NULL);

    if (size % 32 == 0) {
        *padding_l = 0;
        *padding_r = 0;
    } else {
        int padding = 32 - (size % 32);
        *padding_l = padding / 2;
        *padding_r = padding / 2 + (padding % 2 == 0 ? 0 : 1);
    }
}

void convert_image_to_bits(unsigned char *pixel_bits,
                           const unsigned char *image_data,
                           const int w,
                           const int h,
                           int *bitmap_w,
                           int *bitmap_h)
{
    assert(pixel_bits != NULL);
    assert(image_data != NULL);
    assert(bitmap_w != NULL);
    assert(bitmap_h != NULL);

    int padding_l, padding_r, padding_t, padding_b;
    calculate_padding(w, &padding_l, &padding_r);
    calculate_padding(h, &padding_t, &padding_b);

    int padded_w = w + padding_l + padding_r;

    // We only need to add the padding to the bottom for height.
    // This is because when printing long images, only the last
    // chunk will have the irregular height.
    padding_b += padding_t;

    for (int y = 0; y < h; y++) {
        for (int x = 0; x < padded_w; x++) {
            int pi = (y * padded_w) + x;
            int curr_byte = pi / 8;
            unsigned char pixel = image_data[(y * w) + x];
        #if 0  // 可以打印浅色， 但效果不好
            unsigned char r, g, b;
            r = (pixel & 0xe0) >> 5;
            g = (pixel & 0x1c) >> 2;
            b = (pixel & 03);
            float rgb = 0.3*r + 0.59*g + 0.11*b; 
            int bit =   y < h ? rgb < 3 : 0;
        #else
            int bit = y < h ? pixel < 128 : 0;
        #endif
            set_bit(&pixel_bits[curr_byte], 7 - (pi % 8), bit);
        }
    }

    for (int y = 0; y < padding_b; y++) {
        for (int x = 0; x < padded_w; x++) {
            int pi = (h * padded_w) + (y * padded_w) + x;
            int curr_byte = pi / 8;
            set_bit(&pixel_bits[curr_byte], 7 - (pi % 8), 0);
        }
    }

    // Outputs the bitmap width and height after padding
    *bitmap_w = w + padding_l + padding_r;
    *bitmap_h = h + padding_b;
}